`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: Wenting Zhang
// 
// Create Date:    15:28:43 02/07/2018 
// Design Name: 
// Module Name:    dvi_mixer 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module dvi_mixer(
    input clk,
    input rst,
    // GameBoy Image Input
    input gb_hs,
    input gb_vs,
    input gb_pclk,
    input [1:0] gb_pdat,
    input gb_valid,
    // Debugger Char Input
    output [6:0] dbg_x,
    output [4:0] dbg_y,
    input [6:0] dbg_char,
    output dbg_sync,
    // DVI signal Output
    output dvi_hs,
    output dvi_vs,
    output dvi_blank,
    output reg [7:0] dvi_r,
    output reg [7:0] dvi_g,
    output reg [7:0] dvi_b,
    // Debug
    input input_disable
    );
    
    localparam GB_LIGHT = 24'h8b9a26; // Used for pixel 11
    localparam GB_MID3  = 24'h658635;
    localparam GB_MID2  = 24'h456a3e;
    localparam GB_MID1  = 24'h2d4b39;
    localparam GB_DARK  = 24'h212f25; // Used for pixel 00
    localparam GB_BACK  = 24'hbe9e16;

    //Decoded GameBoy Input colors
    wire [7:0] gb_r;
    wire [7:0] gb_g;
    wire [7:0] gb_b;
    wire [7:0] gb_grid_r;
    wire [7:0] gb_grid_g;
    wire [7:0] gb_grid_b;

    //Background colors
    wire [7:0] bg_r;
    wire [7:0] bg_g;
    wire [7:0] bg_b;

    //X,Y positions generated by the timing generator
    wire [10:0] dvi_x;
    wire [10:0] dvi_y;
    
    //X,Y positions of GB display
    wire [7:0] gb_x;
    wire [7:0] gb_y;
    wire gb_grid; // If it's on grid line

    //VGA font
    wire [6:0] font_ascii;
    wire [3:0] font_row;
    wire [2:0] font_col;
    wire font_pixel;

    //Final pixel output
    wire [7:0] out_r;
    wire [7:0] out_g;
    wire [7:0] out_b;

    wire signal_in_gb_range;
    assign out_r = (signal_in_gb_range) ? ((gb_grid) ? (gb_grid_r) : (gb_r)) : (bg_r);
    assign out_g = (signal_in_gb_range) ? ((gb_grid) ? (gb_grid_g) : (gb_g)) : (bg_g);
    assign out_b = (signal_in_gb_range) ? ((gb_grid) ? (gb_grid_b) : (gb_b)) : (bg_b);
    
    wire signal_in_text_range = ((dvi_y <= 20) || (dvi_y >= 460));

    always @(negedge clk)
    begin
      dvi_r <= out_r;
      dvi_g <= out_g;
      dvi_b <= out_b;
    end

    // Font
    localparam font_fg_color = 8'hFF;
    localparam font_bg_color = 8'h20;
    assign dbg_x[6:0] = dvi_x[9:3];
    assign dbg_y[4:0] = dvi_y[8:4];
    assign font_ascii[6:0] = dbg_char[6:0];
    assign font_row[3:0] = dvi_y[3:0];
    assign font_col[2:0] = dvi_x[2:0];
    wire [7:0] text_r = (font_pixel) ? (font_fg_color) : (font_bg_color);
    wire [7:0] text_g = (font_pixel) ? (font_fg_color) : (font_bg_color);
    wire [7:0] text_b = (font_pixel) ? (font_fg_color) : (font_bg_color);
    assign dbg_sync = dvi_vs;
    assign bg_r[7:0] = (signal_in_text_range) ? (text_r) : (GB_BACK[23:16]);
    assign bg_g[7:0] = (signal_in_text_range) ? (text_g) : (GB_BACK[15:8]);
    assign bg_b[7:0] = (signal_in_text_range) ? (text_b) : (GB_BACK[7:0]);

    // Gameboy Input
    reg [7:0] gb_v_counter;
    reg [7:0] gb_h_counter;
    reg latched_input_disable;
    
    //reg [1:0] gb_buffer [0:23039];
    reg [5:0] gb_buffer [0:23039]; // 6 bit depth, with STN response emulation
    wire gb_wr_valid = (gb_v_counter > 8'd0);
    wire [14:0] gb_wr_addr = ((gb_v_counter > 8'd0)?(gb_v_counter - 8'd1):8'd0) * 160 + gb_h_counter;
    reg [5:0] gb_wr_exist_data;
    
    reg gb_vs_last;
    reg gb_hs_last;
    reg gb_pclk_last;
    
    always @(posedge clk)
    begin
        if (rst) begin
            gb_vs_last <= 0;
            gb_hs_last <= 0;
            gb_pclk_last <= 0;
        end
        else begin
            gb_vs_last <= gb_vs;
            gb_hs_last <= gb_hs;
            gb_pclk_last <= gb_pclk;
        end
    end
    
    wire [5:0]gb_target_color = (gb_pdat == 2'b11) ? (6'b000000) :
                               ((gb_pdat == 2'b10) ? (6'b010101) :
                               ((gb_pdat == 2'b01) ? (6'b101010) : (6'b111111)));
                               
    wire [6:0]gb_delta_color = ({1'b0, gb_target_color} + {1'b1, ~gb_wr_exist_data} + 7'b0000001);
    wire [5:0]gb_next_color  = gb_wr_exist_data[5:0] + {gb_delta_color[6], gb_delta_color[6:2]};
    
    always @(posedge clk)
    begin
        if (rst) begin
            gb_v_counter <= 0;
            gb_h_counter <= 0;
        end
        else begin
            if ((gb_vs_last == 1)&&(gb_vs == 0)) begin
                gb_v_counter <= 0;
                latched_input_disable <= input_disable;
            end
            else if ((gb_hs_last == 1)&&(gb_hs == 0)) begin
                gb_h_counter <= 0;
                gb_v_counter <= gb_v_counter + 1'b1;
            end 
            else if (gb_valid) begin
                if ((gb_pclk_last == 0)&&(gb_pclk == 1)) begin
                    gb_h_counter <= gb_h_counter + 1'b1;
                    if ((gb_wr_valid)&&(!latched_input_disable)) begin
                        gb_buffer[gb_wr_addr] <= gb_next_color;   
                        //gb_buffer[gb_wr_addr] <= gb_target_color;
                    end
                end
            end
        end
    end

    wire [14:0] gb_rd_addr = gb_y * 160 + gb_x;
    reg [5:0] gb_rd_data;
    
    always @ (posedge clk)
    begin
        gb_rd_data <= gb_buffer[gb_rd_addr];
    end
    
    always @ (negedge clk)
    begin
        gb_wr_exist_data <= gb_buffer[gb_wr_addr];
    end
    
    // Assume the input brightness range is 0 - 63 (6bit) and will get mapped into 8bits
    // To get the correct color, it is not a single piece of linear.
    //    Iout = (Iin / 63) * (BRI - DAK) + DAK; 
    // When it's border, it should be Iout = Inormal * 0.6 + Ibg * 0.4 -> (Inormal * 10 + Ibg * 6) / 16;
    
    wire [5:0]gb_l_raw = gb_rd_data;
    reg [23:0] gb_lup;
    reg [23:0] gb_ldn;
    always@(*)
    begin
        case (gb_l_raw[5:4])
            2'b00: begin gb_lup = GB_MID1;  gb_ldn = GB_DARK; end
            2'b01: begin gb_lup = GB_MID2;  gb_ldn = GB_MID1; end
            2'b10: begin gb_lup = GB_MID3;  gb_ldn = GB_MID2; end
            2'b11: begin gb_lup = GB_LIGHT; gb_ldn = GB_MID3; end
        endcase
    end
    assign gb_r[7:0] = (gb_l_raw[3:0] * (gb_lup[23:16] - gb_ldn[23:16]) / 16) + gb_ldn[23:16];
    assign gb_g[7:0] = (gb_l_raw[3:0] * (gb_lup[15: 8] - gb_ldn[15: 8]) / 16) + gb_ldn[15: 8];
    assign gb_b[7:0] = (gb_l_raw[3:0] * (gb_lup[ 7: 0] - gb_ldn[ 7: 0]) / 16) + gb_ldn[ 7: 0];
    assign gb_grid_r[7:0] = ((gb_r[7:0] * 10) + (GB_BACK[23:16] * 6)) / 16;
    assign gb_grid_g[7:0] = ((gb_g[7:0] * 10) + (GB_BACK[15: 8] * 6)) / 16;
    assign gb_grid_b[7:0] = ((gb_b[7:0] * 10) + (GB_BACK[ 7: 0] * 6)) / 16;
    
    //BE9E16
    dvi_timing dvi_timing(
      .clk(clk),
      .rst(rst),
      .hs(dvi_hs),
      .vs(dvi_vs),
      .vsi(gb_vs),
      .x(dvi_x),
      .y(dvi_y),
      .gb_x(gb_x),
      .gb_y(gb_y),
      .gb_grid(gb_grid),
      .gb_en(signal_in_gb_range),
      .enable(dvi_blank)
      //.address()
    );

    vga_font vga_font(
      .clk(clk),
      .ascii_code(font_ascii),
      .row(font_row),
      .col(font_col),
      .pixel(font_pixel)
    );    
    
endmodule
